package v1

import (
	"context"

	"google.golang.org/grpc/codes"
	"google.golang.org/grpc/status"
	"google.golang.org/protobuf/types/known/emptypb"

	"quipus/internal/database/model"
	"quipus/internal/utils"
	v1pb "quipus/proto/gen/api/v1"
	apb "quipus/proto/gen/assist"
	cpb "quipus/proto/gen/common"
)

func (s *APIV1Service) ListKnotReactions(ctx context.Context, request *v1pb.ListKnotReactionsRequest) (*v1pb.ListKnotReactionsResponse, error) {
	knotId, err := utils.ExtractIDFromUrn(utils.KnotUrnPrefix, request.Urn)
	if err != nil {
		return nil, status.Errorf(codes.InvalidArgument, "invalid knot name")
	}
	reactions, err := s.Gds.Handler.ListReactions(ctx, &apb.FindReaction{
		KnotId: knotId,
	})

	if err != nil {
		return nil, status.Errorf(codes.Internal, "failed to list reactions")
	}

	response := &v1pb.ListKnotReactionsResponse{
		Reactions: []*v1pb.Reaction{},
	}
	for _, reaction := range reactions {
		reactionMessage, err := s.convertReactionFromDB(ctx, reaction)
		if err != nil {
			return nil, status.Errorf(codes.Internal, "failed to convert reaction")
		}
		response.Reactions = append(response.Reactions, reactionMessage)
	}
	return response, nil
}

func (s *APIV1Service) UpsertKnotReaction(ctx context.Context, request *v1pb.UpsertKnotReactionRequest) (*v1pb.Reaction, error) {
	user, err := s.GetCurrentUser(ctx)
	if err != nil {
		return nil, status.Errorf(codes.Internal, "failed to get current user")
	}

	knotId, err := utils.ExtractIDFromUrn(utils.KnotUrnPrefix, request.Knot)
	if err != nil {
		return nil, status.Errorf(codes.InvalidArgument, "invalid knot name")
	}

	reaction := &model.Reaction{
		CreatorID:    user.ID,
		KnotID:       knotId,
		ReactionType: request.ReactionType.String(),
	}

	err = s.Gds.Handler.UpsertReaction(ctx, reaction)
	if err != nil {
		return nil, status.Errorf(codes.Internal, "failed to upsert reaction")
	}

	reactionMessage, err := s.convertReactionFromDB(ctx, reaction)
	if err != nil {
		return nil, status.Errorf(codes.Internal, "failed to convert reaction")
	}
	return reactionMessage, nil
}

func (s *APIV1Service) DeleteKnotReaction(ctx context.Context, request *v1pb.DeleteKnotReactionRequest) (*emptypb.Empty, error) {
	delete := &apb.DeleteReaction{}

	if request.Urn != "" {
		id, err := utils.ExtractIDFromUrn(utils.ReactionUrnPrefix, request.Urn)
		if err != nil {
			return nil, status.Errorf(codes.InvalidArgument, "invalid reaction name")
		}
		delete.Id = id
	}

	if request.Knot != "" {
		knotId, err := utils.ExtractIDFromUrn(utils.KnotUrnPrefix, request.Knot)
		if err != nil {
			return nil, status.Errorf(codes.InvalidArgument, "invalid knot name")
		}
		delete.KnotId = knotId
	}

	if request.ReactionType != cpb.ReactionType_REACTION_TYPE_UNSPECIFIED {
		delete.Action = request.ReactionType.String()
	}
	if err := s.Gds.Handler.DeleteReaction(ctx, delete); err != nil {
		return nil, status.Errorf(codes.Internal, "failed to delete reaction")
	}

	return &emptypb.Empty{}, nil
}

func (s *APIV1Service) convertReactionFromDB(ctx context.Context, reaction *model.Reaction) (*v1pb.Reaction, error) {
	creator, err := s.Gds.Handler.GetUser(ctx, &apb.FindUser{
		Id: reaction.CreatorID,
	})
	if err != nil {
		return nil, err
	}

	return &v1pb.Reaction{
		Urn:          utils.BuildUrn(utils.ReactionUrnPrefix, reaction.ID),
		Creator:      utils.BuildUrn(utils.UserUrnPrefix, creator.ID),
		Knot:         utils.BuildUrn(utils.KnotUrnPrefix, reaction.KnotID),
		ReactionType: cpb.ReactionType(cpb.ReactionType_value[reaction.ReactionType]),
	}, nil
}
